package com.mh.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mh.dao.BillDao;
import com.mh.dao.CategoryDao;
import com.mh.dto.vo.BillFilterVo;
import com.mh.dto.vo.CalendarDayDataOfMonth;
import com.mh.dto.vo.R;
import com.mh.dto.vo.BillShowVo;
import com.mh.pojo.Bill;
import com.mh.pojo.Category;
import com.mh.service.BillService;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;

/**
 * 账单表(Bill)表服务实现类
 *
 * @author zmh
 * @since 2025-02-08 09:18:50
 */
@Service
@RequiredArgsConstructor
public class BillServiceImpl extends ServiceImpl<BillDao, Bill> implements BillService {


    private final BillDao billDao;

    private final RabbitTemplate rabbitTemplate;

    private final CategoryDao categoryDao; // 账单分类操作


    /**
     * 分页查询
     *
     * @param page 查询页数
     * @param size 一页显示条数
     * @return ·
     */
    public R<Page<Bill>> getAllByPage(int page, int size) {
        Page<Bill> billPage = new Page<>(page, size);
        LambdaQueryWrapper<Bill> queryWrapper = new LambdaQueryWrapper<>();
        //TODO 查询条件定制
        return R.success(billDao.selectPage(billPage, queryWrapper));
    }

    /**
     * 添加账单
     * @param bill 账单数据
     * @return ·
     */
    @Override
    public R<Boolean> addBill(Bill bill) {
        // 添加账单
        int insert = billDao.insert(bill);
        if (insert <= 0) {
            return R.error("添加账单失败");
        }

        // 添加成功，异步处理 资产账户表 和 资产变动表
        rabbitTemplate.convertAndSend("addBill", bill);
        return R.success(true);
    }

    /**
     * 获取账单数据
     * @param showPosition 账单组件位置：index-首页 all_bill：全部账单(过滤项)
     * @param billFilterVo 过滤项DTO -当账单组件显示在首页时all_bill过滤箱参数对象为空
     * @return ·
     */
    @Override
    public R<List<BillShowVo>> getBillData(String showPosition, BillFilterVo billFilterVo) {
        // 构建查询条件
        LambdaQueryWrapper<Bill> queryWrapper = new LambdaQueryWrapper<>();

        // 当显示位置是首页时，展示最近七天的账单
        if ("index".equals(showPosition)) {
            // 获取当前日期的七天前时间
            LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7);
            queryWrapper.eq(Bill::getBookId, billFilterVo.getAccountBookId());
            queryWrapper.ge(Bill::getTransactionTime, sevenDaysAgo);  // 过滤交易时间大于等于七天前的账单

        } else if ("all_bill".equals(showPosition) && billFilterVo != null) {
            // 当显示位置是全部账单页面时，使用 BillFilterVo 中的过滤条件

            // 账本ID
            if (billFilterVo.getAccountBookId() != null && billFilterVo.getAccountBookId() != 0) {
                queryWrapper.eq(Bill::getBookId, billFilterVo.getAccountBookId());
            }

            // 月份条件（如：2025-02）
            if (billFilterVo.getMonth() != null && !billFilterVo.getMonth().isEmpty()) {
                YearMonth yearMonth = YearMonth.parse(billFilterVo.getMonth(), DateTimeFormatter.ofPattern("yyyy-MM"));
                LocalDateTime startOfMonth = yearMonth.atDay(1).atStartOfDay();
                LocalDateTime endOfMonth = yearMonth.atEndOfMonth().atTime(23, 59, 59);

                queryWrapper.between(Bill::getTransactionTime, startOfMonth, endOfMonth);
            }

            // 开始时间和结束时间条件
            if (billFilterVo.getStartTime() != null && billFilterVo.getEndTime() != null) {
                queryWrapper.between(Bill::getTransactionTime, billFilterVo.getStartTime(), billFilterVo.getEndTime());
            }

            // 收支类型条件（1-收入 2-支出）
            if (billFilterVo.getIeType() != null) {
                queryWrapper.eq(Bill::getType, billFilterVo.getIeType());
            }

            // 账户类型条件 - 传入账户ID
            if (billFilterVo.getAccountType() != null) {
                queryWrapper.eq(Bill::getAccountId, billFilterVo.getAccountType());
            }
        }

        // 按照时间降序排序
        queryWrapper.orderByDesc(Bill::getTransactionTime);

        // 执行查询并返回结果
        List<Bill> bills = billDao.selectList(queryWrapper);

        // 遍历查询账单分类的ICON补充到DTO中
        ArrayList<BillShowVo> billShowVos = new ArrayList<>();
        for (Bill bill : bills) {
            BillShowVo billShowVo = new BillShowVo();
            // 拷贝账单基本信息
            BeanUtils.copyProperties(bill, billShowVo);
            // 查询账单分类中的图标字段
            Category category = categoryDao.selectById(bill.getCategoryId());
            billShowVo.setIconName(category.getIcon()); // 设置分类图标
            billShowVo.setTypeName(category.getName()); // 设置分类名称
            billShowVos.add(billShowVo);
        }
        return R.success(billShowVos);
    }

    /**
     * 获取月份的每天收支情况（修正版：返回每日净收支）
     * @param yearMonth 年月
     * @return 每日净收支（当日收入-支出）
     */
    @Override
    public R<Map<String, Double>> getCalendarDayDataOfMonth(String yearMonth) {
        YearMonth yearMonthObj = YearMonth.parse(yearMonth, DateTimeFormatter.ofPattern("yyyy-MM"));
        LocalDateTime startOfMonth = yearMonthObj.atDay(1).atStartOfDay();
        LocalDateTime endOfMonth = yearMonthObj.atEndOfMonth().atTime(23, 59, 59);

        // 查询账单数据
        LambdaQueryWrapper<Bill> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.between(Bill::getTransactionTime, startOfMonth, endOfMonth);
        List<Bill> bills = billDao.selectList(queryWrapper);

        // 1. 按天统计净收支
        Map<String, Double> dailyNetMap = new HashMap<>();
        for (Bill bill : bills) {
            String dateKey = bill.getTransactionTime().toLocalDate().toString();
            double amount = bill.getType() == 1 ? bill.getAmount() : -bill.getAmount();
            dailyNetMap.put(dateKey, dailyNetMap.getOrDefault(dateKey, 0.0) + amount);
        }

        // 2. 生成全量日期并填充数据
        Map<String, Double> result = new LinkedHashMap<>();
        LocalDate current = yearMonthObj.atDay(1);
        while (!current.isAfter(yearMonthObj.atEndOfMonth())) {
            String dateKey = current.toString();
            result.put(dateKey, dailyNetMap.getOrDefault(dateKey, 0.0));
            current = current.plusDays(1);
        }

        return R.success(result);
    }

    /**
     * 根据日期获取当天所有账单
     * @param date 日期字符串（格式：yyyy-MM-dd）
     * @return 当天账单列表
     */
    @Override
    public R<List<BillShowVo>> getBillByDate(String date) {
        try {
            // 1. 解析日期并生成时间范围
            LocalDate queryDate = LocalDate.parse(date, DateTimeFormatter.ISO_DATE);
            LocalDateTime startOfDay = queryDate.atStartOfDay();
            LocalDateTime endOfDay = queryDate.atTime(23, 59, 59);

            // 2. 构建查询条件
            LambdaQueryWrapper<Bill> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper
                    .between(Bill::getTransactionTime, startOfDay, endOfDay)
                    .orderByAsc(Bill::getTransactionTime); // 按交易时间排序

            // 3. 执行查询
            List<Bill> bills = billDao.selectList(queryWrapper);

            // 遍历查询账单分类的ICON补充到DTO中
            ArrayList<BillShowVo> billShowVos = new ArrayList<>();
            for (Bill bill : bills) {
                BillShowVo billShowVo = new BillShowVo();
                // 拷贝账单基本信息
                BeanUtils.copyProperties(bill, billShowVo);
                // 查询账单分类中的图标字段
                Category category = categoryDao.selectById(bill.getCategoryId());
                billShowVo.setIconName(category.getIcon()); // 设置分类图标
                billShowVo.setTypeName(category.getName()); // 设置分类名称
                billShowVos.add(billShowVo);
            }

            // 4. 处理空数据（非必需，根据业务需求）
            if (bills.isEmpty()) {
                return R.success(Collections.emptyList());
            }

            return R.success(billShowVos);
        } catch (DateTimeParseException e) {
            // 日期格式错误处理
            return R.error("日期格式不正确，请使用yyyy-MM-dd格式");
        } catch (Exception e) {
            // 其他异常处理
            return R.error("系统繁忙，请稍后重试");
        }
    }

}

